/*
 * @Author: hongbin
 * @Date: 2022-11-02 18:52:29
 * @LastEditors: hongbin
 * @LastEditTime: 2022-12-01 12:13:49
 * @Description:socket管理
 */
import { io, Socket } from "socket.io-client";

export class SocketManagerClass {
    private socketClient!: Socket;
    private connect_error: (err: Error) => void = (err) => console.log("socket 连接错误", err);
    private connect_success: VoidFunction = () => console.log("socket 连接成功");
    listenBacklog: { [key: string]: (...args: any[]) => void }[] = [];
    emitBacklog: { [key: string]: (...args: any[]) => void }[] = [];
    listenOnceBacklog: { [key: string]: (...args: any[]) => void }[] = [];

    constructor() {
        //! 如果放在构造函数中每次热重载都会累积回调 原因不明
        // this.socketClient = io("http://localhost:3001/", { autoConnect: false });
    }

    /**
     * 初始化socket管理工具 进行socket连接 监听事件
     */
    init() {
        //不自动连接 手动调用connect方法连接
        // this.socketClient = io("http://hongbin.xyz:3001/", { autoConnect: false });
        this.socketClient = io(`${process.env.NEXT_PUBLIC_SOCKET_HREF}`, { autoConnect: false });
        //先设置监听再手动连接，防止错开触发函数
        this._setConnectError();
        this._setConnectSuccess();
        // this._listen();

        //手动连接 让前面的操作先执行 保证生效
        this.socketClient.connect();
        return this;
    }

    /**
     * 设置事件监听回调 同socket.on(ev,listener)
     */
    listen(eventName: string, listener: (...args: any[]) => void) {
        //如果没实例化 先放到挤压队列中 初始化后在添加
        if (!this.socketClient) {
            // console.log("积压监听:", eventName);
            this.listenBacklog.push({ [eventName]: listener });
        } else {
            this.socketClient.on(eventName, listener);
        }
        return this;
    }
    /**
     * 移除某事件监听 回调必须和监听的回调一致
     */
    removeListen(eventName: string, listener: (...args: any[]) => void) {
        this.socketClient.off(eventName, listener);
        return this;
    }

    /**
     * 只监听一次的事件
     */
    listenOnce(eventName: string, listener: (...args: any[]) => void) {
        if (!this.socketClient) {
            this.listenOnceBacklog.push({ [eventName]: listener });
        } else {
            this.socketClient.once(eventName, listener);
        }
        return this;
    }

    /**
     * 发送事件 同socket.emit(...args)
     */
    emit(...args: Parameters<Socket["emit"]>) {
        if (!this.socketClient) {
            this.emitBacklog.push({ [args[0]]: args[1] });
        } else {
            this.socketClient.emit(...args);
        }
    }

    /**
     * 监听全部消息
     */
    private _listenAll() {
        this.socketClient.onAny((...args) => {
            console.log(args);
        });
    }

    /**
     * 监听连接成功
     */
    private _setConnectSuccess() {
        this.socketClient.on("connect", this.connect_success);
    }

    /**
     * 监听连接错误
     */
    private _setConnectError() {
        this.socketClient.on("connect_error", this.connect_error);
    }

    /**
     * 设置连接成功回调
     */
    success(callback: VoidFunction) {
        this.connect_success = () => {
            callback();
            this.listenBacklog.forEach((work) => {
                const [event, listener] = Object.entries(work)[0];
                this.listen(event, listener);
            });
            this.listenBacklog = [];

            this.emitBacklog.forEach((work) => {
                const [event, listener] = Object.entries(work)[0];
                this.emit(event, listener);
            });
            this.emitBacklog = [];
            this.listenOnceBacklog.forEach((work) => {
                const [event, listener] = Object.entries(work)[0];
                this.listenOnce(event, listener);
            });
            this.listenOnceBacklog = [];
        };
        return this;
    }

    /**
     * 设置连接错误回调
     */
    error(callback: (err: Error) => void) {
        this.connect_error = callback;
        return this;
    }

    /**
     * 断开连接
     */
    disconnect() {
        this.socketClient?.disconnect();
        this.socketClient?.close();
    }
}

/**
 * socket 管理工具
 */
export const SocketManager = new SocketManagerClass();
